package cn.shadow.aop;

import cn.shadow.aop.advice.*;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.List;

/**
 * 生成AOP通知链
 * @author 冯啸迪
 */
public class AopAdviceChainInvocation {

    private static Method invokeMethod;
    static {
        try {
            invokeMethod = AopAdviceChainInvocation.class.getMethod("invoke", null);
        } catch (NoSuchMethodException | SecurityException e) {
            e.printStackTrace();
        }
    }

    private Object proxy;

    private Object target;

    private Method method;

    private Object[] args;

    List<Object> adviceList;

    public AopAdviceChainInvocation(Object proxy, Object target, Method method, Object[] args, List<Object> adviceList) {
        this.proxy = proxy;
        this.target = target;
        this.method = method;
        this.args = args;
        this.adviceList = adviceList;
    }

    private int i = 0;

    /**
     * 这个方法直接通过递归的形式进行循环了所以内部不需要在写循环
     * @return
     */
    public Object invoke() throws Throwable {
        if (i < adviceList.size()){
            Advice a = (Advice) adviceList.get(i++);
            if (a instanceof BeforeAdvice){
                // 前置通知，无需考虑什么，直接做就好
                ((BeforeAdvice) a).before(method, args, target);
            } else if (a instanceof MethodInterceptor){
                return ((MethodInterceptor)a).invoke(invokeMethod, null, null);
            } else if (a instanceof AfterReturningAdvice){
                Object returnObject = this.invoke();
                return ((AfterReturningAdvice) a).afterReturning(returnObject, method, args, target);
            } else if (a instanceof AfterAdvice){
                Object returnObject = null;
                try {
                    returnObject = this.invoke();
                } finally {
                    ((AfterAdvice) a).after(returnObject, method, args, target);
                }
                return returnObject;
            } else if (a instanceof ThrowsAdvice){
                try {
                    return this.invoke();
                } catch (Exception e){
                    ((ThrowsAdvice) a).afterThrowing(method, args, target, e);
                }
            }
            return this.invoke();
        } else {
            // 所有的通知都执行完了，而且没用执行原来的方法
            return method.invoke(target, args);
        }
    }
}
